package predictionEngine;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class TreeNode {
    private int depth=0;
    private double value;
    private TreeNode parent,left,right;
    private String attributeName;
    private ISplit split;
    private int nrOfInstances=0;
    protected static int MIN_INSTANCES = 1;
    

    public TreeNode(TreeNode parent, Dataset dataset, int nrOfConsideredAttributes) {
        if (parent != null)
            depth = parent.depth + 1;

        if (dataset.size() >
            MIN_INSTANCES) { //Find the best split and continue recursivly for each split

            split = findBestSplit(dataset, nrOfConsideredAttributes);
            Dataset[] children = dataset.split(split);
            if (children[0].size() >= MIN_INSTANCES &&
                children[1].size() >= MIN_INSTANCES) {
                left = new TreeNode(this, children[0], nrOfConsideredAttributes);
                right = new TreeNode(this, children[1], nrOfConsideredAttributes);
            } else {
                split=null;
                calcLeafNodeValue(dataset);
            }
        } else { //leaf node calculate value
            calcLeafNodeValue(dataset);
        }

    }
    public TreeNode(TreeNode parent, Dataset dataset) {
        this(parent,dataset,dataset.getNumberOfAttributes()-1);
    }
    
    
    private void calcLeafNodeValue(Dataset dataset){
        List<double[]>instances = dataset.getInstances();
        double sum=0;
        for(double[] d:dataset.getInstances())
            sum+=d[dataset.targetIndex()];
        value = sum/dataset.size();
        nrOfInstances = dataset.size();
    }

    private ISplit findBestSplit(Dataset dataset,
                                 int nrOfConsideredAttributes) {
        ArrayList<Integer> consideredAttributes = new ArrayList<Integer>(dataset.getNumberOfAttributes());
        int attribute;
        ISplit split;
                
        for (int i = 0; i < dataset.getNumberOfAttributes() - 1; i++)
            consideredAttributes.add(i);
        Collections.shuffle(consideredAttributes);
        split = new NumericSplit(dataset, consideredAttributes.get(0));


        for (int i = 1; i < nrOfConsideredAttributes; i++) {
            ISplit newSplit;
            attribute = consideredAttributes.get(i);
            if (dataset.getAttribute(attribute).isNumeric())
                newSplit = new NumericSplit(dataset, attribute);
            else
                newSplit = new NominalSplit(dataset, attribute);

            if (newSplit.getStrength() > split.getStrength())
                split = newSplit;
        }
        return split;
    }

    public double execute(double[] instance){
        if(left==null)
            return value;
        else if(split.test(instance))
            return left.execute(instance);
        else
            return right.execute(instance);
    }
    
    public String toString(){        
        String s="";
        String tab ="";
        for(int i = 0;i<depth;i++)
            tab+="|   ";
        if(left!=null){
            s+=tab+"+"+split + "\n" +left.toString();        
            s+=tab+"-"+split + "\n" + right.toString();
        }else{
            s+=tab+ "-> " + value+" #"+nrOfInstances+"\n";
        }                
        return s;
    }
    
}
